嗨~大家好,我是阿華今天又來跟大家聊聊~
在查找變數時,javascript 所做的不僅僅是查看目前執行上下文的變數環境。每個執行上下文都有對其外部環境的引用,該外部環境稱為詞法環境。
變數環境是詞法環境的一部分,是目前執行上下文的環境。
閉包是 JavaScript 中一種重要的特性,
它指的是函數包裹函數時,內部函數有使用外部函數的作用域中的變數,即使外部函數已經執行完畢,依然保持對這些變數的引用。
閉包這種特性,在需要維持函數內部狀態或在內部函數需要訪問外部函數作用域的情況下就可以使用。
閉包這種特性使得函數可以保持狀態、延長變數生命週期,並且可以創建私有變數和函數,這對於開發非常有用。
當內部函數引用了外部函數的變數時,JavaScript 引擎會將這些變數的引用保留在內部函數的作用域鍊(scope chain -> Closure)中,即使外部函數已經執行完畢,這些變數仍然可以被內部函數訪問和使用。
下面透過兩個例子來說明閉包,保持狀態、創建私有變數和方法
// 保持狀態、延長變數生命週期
function useState(initialState) {
let state = initialState; // 本該被gc回收的變數現在被保存在內部函數的作用域鍊裡
function getState() {
return state;
}
function setState(updatedState) {
state = updatedState;
}
return [getState, setState];
}
const [count, setCount] = useState(0);
count(); // 0
setCount(1);
count(); // 1
setCount(500);
count(); // 500
// 需要實現私有函數或私有變數的時候(外部數據取不到屬於他 scope 的值)
var counter = (function () {
var privateCounter = 0;
function changeBy(val) {
privateCounter += val;
}
return {
increment: function () {
changeBy(1);
},
decrement: function () {
changeBy(-1);
},
getValue: function () {
return privateCounter;
},
value: privateCounter,
};
})();
console.log(counter.getValue()); // logs 0
counter.increment();
console.log(counter.getValue()); // logs 1
console.log(counter.value); // logs 0
雖然說閉包很好用,但也不是沒有缺點。從記憶體的角度來看,閉包的缺點是顯而易見的,由於閉包會讓內部函數記得外部的變數,這可能會造成變數常駐在記憶體當中,如果使用過多可能會造成記憶體泄露 (memory leak),需要小心使用。
memory leak: 程式沒能成功釋放棄用的記憶體,造成效能的損失或是執行的失敗。